package com.hmall.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.hmall.client.ItemClient;
import com.hmall.common.dto.PageDTO;
import com.hmall.pojo.Item;
import com.hmall.search.pojo.ItemDoc;
import com.hmall.search.pojo.RequestParams;
import com.hmall.search.service.SearchService;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class SearchServiceimpl implements SearchService {

    @Autowired
    private RestHighLevelClient restClient;

    @Autowired
    ItemClient itemClient;



    private QueryBuilder getQueryBuilder(RequestParams params) {
        BoolQueryBuilder boolQueryBuilder=QueryBuilders.boolQuery();
        //构建查询条件
        QueryBuilder queryBuilder=QueryBuilders.boolQuery();
        // 根据传入的关键字查询
        if (!StringUtils.isEmpty(params.getKey())) {
            queryBuilder = QueryBuilders.matchQuery("all", params.getKey());
        }
        boolQueryBuilder.must(queryBuilder);
        //添加品牌过滤条件
        if (!StringUtils.isEmpty(params.getBrand())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("brand", params.getBrand()));
        }
        //添加分类过滤条件
        if (!StringUtils.isEmpty(params.getCategory())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("category", params.getCategory()));
        }
        //添加价格过滤条件
        if (params.getMinPrice() != null) {
            boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(params.getMinPrice()));
        }
        if (params.getMaxPrice() != null) {
            boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").lte(params.getMaxPrice()));
        }
        return boolQueryBuilder;
    }
    @Override
    public PageDTO<ItemDoc> list(RequestParams params) {
        //根据传入的关键词分页查询酒店数据
        SearchRequest request = new SearchRequest("hmall");
        //构建查询对象
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        //构建指定条件
        QueryBuilder queryBuilder=getQueryBuilder(params);

        //添加相关算法
        FunctionScoreQueryBuilder functionScoreQuery = QueryBuilders.functionScoreQuery(queryBuilder, new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                        QueryBuilders.termQuery("isAD", true),
                        ScoreFunctionBuilders.weightFactorFunction(10)
                )
        });
        //指定分页条件
        int from = (params.getPage() - 1) * params.getSize();
        sourceBuilder.from(from);
        sourceBuilder.size(params.getSize());

        //添加指定条件
        sourceBuilder.query(functionScoreQuery);
        if (params.getSortBy().equals("default")){
            sourceBuilder.sort("_score",SortOrder.DESC);
        }else if (params.getSortBy().equals("sold")){
            sourceBuilder.sort(params.getSortBy(),SortOrder.DESC);
        }else{
            sourceBuilder.sort(params.getSortBy(),SortOrder.ASC);
        }

        //相关性算法,优先已分值排序
        sourceBuilder.sort("_score", SortOrder.DESC);

        if (!StringUtils.isEmpty(params.getKey())) {
            //指定高亮条件
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            //指定高亮字段
            highlightBuilder.field("name");
            //指定前缀和后缀
            highlightBuilder.preTags("<em>");
            highlightBuilder.postTags("</em>");
            //不是必须配置搜索字段
            highlightBuilder.requireFieldMatch(false);
            sourceBuilder.highlighter(highlightBuilder);
        }
//        指定数据源
        request.source(sourceBuilder);
        //发送请求
        try {
            SearchResponse response = restClient.search(request, RequestOptions.DEFAULT);
            //构建对象结果
            PageDTO<ItemDoc> pageResult = new PageDTO<>();
            //分析相应结果
            SearchHits searchHits = response.getHits();//第一层命中的数据
            long total = searchHits.getTotalHits().value;//命中的总条数
            System.out.println("命中的总条数:" + total);
            pageResult.setTotal(total);
            List<ItemDoc> hotelDocs = new ArrayList<>();
            SearchHit[] hits = searchHits.getHits();
            for (SearchHit hit : hits) {
                String sourceAsString = hit.getSourceAsString();//获取文档json字符串
                //将json封装成对象
                ItemDoc hotelDoc = JSON.parseObject(sourceAsString, ItemDoc.class);

                //获取高亮结果
                if (!StringUtils.isEmpty(params.getKey())) {
                    Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                        HighlightField name = highlightFields.get("name");
                        if (name!=null) {
                            String string = name.fragments()[0].string();
                                hotelDoc.setName(string);
                    }
                }
                hotelDocs.add(hotelDoc);
            }
            pageResult.setList(hotelDocs);
            return pageResult;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public Map<String, List<String>> filters(RequestParams param) {
        System.out.println("12345678987654321234567");
        // 返回过滤的数据
        Map<String, List<String>> map = new HashMap<>();
        List<String> category = new ArrayList<>();
        map.put("category", category);
        List<String> brand = new ArrayList<>();
        map.put("brand", brand);
        // 构建查询请求对象 指定查询的索引名
        SearchRequest request = new SearchRequest("hmall");
        // 构建查询源对象
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        // 构建查询条件
        QueryBuilder queryBuilder;
        // 匹配查询  指定要查询的字段,字段值
        // 需要根据查询条件确定聚合范围
        queryBuilder = getQueryBuilder(param);
        // 指定查询条件
        sourceBuilder.query(queryBuilder);
        // 指定聚合条件
        AggregationBuilder agg = AggregationBuilders.terms("categoryAgg");
        ((TermsAggregationBuilder) agg).field("category");
        ((TermsAggregationBuilder) agg).size(100);
        sourceBuilder.aggregation(agg);
        AggregationBuilder starAgg = AggregationBuilders.terms("brandAgg");
        ((TermsAggregationBuilder) starAgg).field("brand");
        ((TermsAggregationBuilder) starAgg).size(50);
        sourceBuilder.aggregation(starAgg);

        // 不获取原始文档
        sourceBuilder.size(0);
        // 指定查询源
        request.source(sourceBuilder);
        // 发送查询请求
        try {
            SearchResponse response = restClient.search(request, RequestOptions.DEFAULT);
            // 分析结果
            Aggregations aggregations = response.getAggregations();
            Terms cityAgg = aggregations.get("categoryAgg");
            for (Terms.Bucket bucket : cityAgg.getBuckets()) {
                String keyAsString = bucket.getKeyAsString();
                category.add(keyAsString);
            }

            Terms starAggResult = aggregations.get("brandAgg");
            for (Terms.Bucket bucket : starAggResult.getBuckets()) {
                String keyAsString = bucket.getKeyAsString();
                brand.add(keyAsString);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }

        return map;
    }

    /**
     * 导入数据
     */
    @Override
    public void sync() {
        //第一次查询总个数
        PageDTO<Item> pageDTO = itemClient.list(1, 1);
        Long total=pageDTO.getTotal();
        Long totalPage=total/1000;
        for (int i = 1; i <= totalPage+1; i++) {
            PageDTO<Item> list = itemClient.list(i, 1000);
            List<Item> items = list.getList();
            BulkRequest bulkRequest=new BulkRequest();
            for (Item item : items) {
                IndexRequest indexRequest=new IndexRequest("hmall");
                indexRequest.id(item.getId().toString());
                indexRequest.source(JSON.toJSONString(item), XContentType.JSON);
                bulkRequest.add(indexRequest);
            }
            try {
                restClient.bulk(bulkRequest,RequestOptions.DEFAULT);
                System.out.println("第"+i+"页导入完成");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 创建索引表
     */
    @Override
    public void createIndex() {

        CreateIndexRequest request=new CreateIndexRequest("hmall");
        request.settings(Settings.builder()
        .put("index.number_of_shards",1)
        .put("index.number_of_replicas",0));

        Map<String,Object> mapping=new HashMap<>();
        Map<String,Object>properties=new HashMap<>();

        Map<String,Object>all=new HashMap<>();
        all.put("type","text");
        all.put("analyzer","ik_max_word");
        properties.put("all",all);

        Map<String,Object>name=new HashMap<>();
        name.put("type","text");
        name.put("analyzer","ik_max_word");
        name.put("copy_to","all");
        properties.put("name",name);

        Map<String,Object>price=new HashMap<>();
        price.put("type","long");
        properties.put("price",price);

        Map<String,Object>stock=new HashMap<>();
        stock.put("type","integer");
        properties.put("stock",stock);

        Map<String ,Object> image=new HashMap<>();
        image.put("type","keyword");
        image.put("index","false");
        properties.put("image",image);

        Map<String,Object>category=new HashMap<>();
        category.put("type","keyword");
        category.put("copy_to","all");
        properties.put("category",category);

        Map<String,Object>brand=new HashMap<>();
        brand.put("type","keyword");
        brand.put("copy_to","all");
        properties.put("brand",brand);

        Map<String,Object>spec=new HashMap<>();
        spec.put("type","keyword");
        properties.put("spec",spec);

        Map<String,Object>sold=new HashMap<>();
        sold.put("type","integer");
        properties.put("sold",sold);

        Map<String,Object> commentCount=new HashMap<>();
        commentCount.put("type","integer");
        properties.put("commentCount",commentCount);

        Map<String,Object>status=new HashMap<>();
        status.put("type","integer");
        properties.put("status",status);

        Map<String,Object>isAD=new HashMap<>();
        isAD.put("type","boolean");
        properties.put("isAD",isAD);

        Map<String,Object>createTime=new HashMap<>();
        createTime.put("type","date");
        properties.put("createTime",createTime);

        Map<String,Object>updateTime=new HashMap<>();
        updateTime.put("type","date");
        properties.put("updateTime",updateTime);

        mapping.put("properties",properties);
        request.mapping(mapping);

        try {
            restClient.indices().create(request,RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
